package com.tj.api.interceptors;

import com.alibaba.fastjson.JSON;
import com.fqgj.common.utils.JSONUtils;
import com.fqgj.exception.common.ApplicationException;
import com.tj.api.annotations.SignIgnore;
import com.tj.api.annotations.VisitorAccessible;
import com.tj.api.utils.AppSignUtil;
import com.tj.bo.account.client.CreateAccountService;
import com.tj.bo.account.client.enums.AccountTypeEnum;
import com.tj.bo.account.client.vo.CreateAccountVO;
import com.tj.bo.common.conf.config.FileConfigUtil;
import com.tj.bo.common.conf.enums.RestCodeEnum;
import com.tj.bo.common.conf.util.HttpUtil;
import com.tj.bo.trade.client.enums.TradeTypeEnum;
import com.tj.bo.user.client.UserQueryService;
import com.tj.bo.user.client.UserRegisterService;
import com.tj.bo.user.client.enums.UserRegisterTypeEnum;
import com.tj.bo.user.client.vo.input.UserRegisterInput;
import com.tj.bo.user.client.vo.output.UserQueryOutput;
import com.tj.template.config.vo.RequestBasicInfo;
import com.tj.template.config.vo.RequestLocalInfo;
import com.tj.template.config.vo.UserAccessInfo;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;

/**
 * @program: binary-option
 * @description
 * @author: binary
 * @create: 2020-06-08 17:00
 **/
@Component
public class AccessInterceptor implements HandlerInterceptor {

    private static final Logger LOGGER = LoggerFactory.getLogger(AccessInterceptor.class);

    @Autowired
    private FileConfigUtil fileConfigUtil;

    @Autowired
    private UserQueryService userQueryService;

    @Autowired
    private UserRegisterService userRegisterService;

    @Autowired
    private CreateAccountService createAccountService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {

        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Content-Type,Accept,X-Requested-With,remember-me,bid,basicParams");

        RequestLocalInfo.clearThreadLocal();
        if (isWebResources(request)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) o;
        SignIgnore signIgnoreAnnotation = handlerMethod.getMethodAnnotation(SignIgnore.class);
        if (signIgnoreAnnotation != null) {
            return true;
        }

        this.verifySignInfo(request);
        RequestBasicInfo basicVO = RequestLocalInfo.getRequestBasicInfo();
        this.verifyToken(basicVO.getRealToken(), handlerMethod, request);

        return true;
    }

    /**
     * 验签
     *
     * @param request
     */
    private void verifySignInfo(HttpServletRequest request) {
        String basicParams = request.getHeader("basicParams");
        if (StringUtils.isEmpty(basicParams)) {
            throw new ApplicationException(RestCodeEnum.SYSTEM_PARAMETER_ERROR);
        }
        RequestBasicInfo basicVO = JSON.parseObject(basicParams, RequestBasicInfo.class);
        if (StringUtils.isEmpty(basicVO.getDeviceId())) {
            throw new ApplicationException(RestCodeEnum.SYSTEM_PARAMETER_ERROR);
        }
        //1594721769247作为一个免签口子 开发人员自测用
        if (!AppSignUtil.verifyAppSign(basicVO, fileConfigUtil.isServerTest()) && !"1594721769247".equals(basicVO.getTimestamp())) {
            throw new ApplicationException(RestCodeEnum.PARAMETER_SIGN_ERROR);
        }
        String ip = HttpUtil.getIpAddr(request);
        LOGGER.info("获取真实ip,deviceId:{},{}", basicVO.getDeviceId(), ip);
        basicVO.setIp(ip);
        RequestLocalInfo.putCurrentRequestBasicVO(basicVO);
    }

    /**
     * token校验
     *
     * @param realToken
     * @param handlerMethod
     * @param request
     */
    private void verifyToken(String realToken, HandlerMethod handlerMethod, HttpServletRequest request) {
        //异常场景 报错
        if (StringUtils.isEmpty(realToken)) {
            VisitorAccessible visitorAccessible = handlerMethod.getMethodAnnotation(VisitorAccessible.class);
            if (visitorAccessible == null) {
                throw new ApplicationException(RestCodeEnum.NEED_TOKEN_ERROR);
            }
        }

        //token合法性校验
        if (!hasPermission(realToken, request)) {
            LOGGER.warn("User authentication failed,realToken:{}", realToken);
            throw new ApplicationException(RestCodeEnum.TOKEN_NOT_AVAILABLE);
        }
    }

    /**
     * token校验
     *
     * @param realToken
     * @param request
     * @return
     */
    private boolean hasPermission(String realToken, HttpServletRequest request) {
        //H5发送验证码
        if(request.getRequestURI().contains("user/email/send") || request.getRequestURI().contains("user/promotion/register")){
            return true;
        }

        try {
            UserAccessInfo userAccessInfo = new UserAccessInfo();
            Integer tradeType = this.getTradeType(request);
            if (StringUtils.isNotEmpty(realToken)) {
                UserQueryOutput userQueryOutput = userQueryService.getUserInfoByRealToken(realToken);
                if (userQueryOutput == null || userQueryOutput.getUserBaseReal() == null) {
                    LOGGER.info("登录失败1,realToken:{}", realToken);
                    throw new ApplicationException(RestCodeEnum.TOKEN_NOT_AVAILABLE);
                }
                if (userQueryOutput == null || userQueryOutput.getUserBaseDemo() == null) {
                    LOGGER.info("登录失败2,realToken:{}", realToken);
                    throw new ApplicationException(RestCodeEnum.TOKEN_NOT_AVAILABLE);
                }
                userAccessInfo.setLoginMode(1);
                userAccessInfo.setUserCode(userQueryOutput.getUserBaseReal().getUserCode());
                userAccessInfo.setUserBaseReal(userQueryOutput.getUserBaseReal());
                userAccessInfo.setUserBaseDemo(userQueryOutput.getUserBaseDemo());
                if (TradeTypeEnum.VIRTUAL_TRADE.getCode().equals(tradeType)) {
                    userAccessInfo.setUserCode(userQueryOutput.getUserBaseDemo().getUserCode());
                }
            } else {
                RequestBasicInfo requestBasicInfo = RequestLocalInfo.getRequestBasicInfo();
                UserQueryOutput userQueryOutput = userQueryService.getUserInfoByDeviceId(requestBasicInfo.getAppCode(), requestBasicInfo.getDeviceId());
                //虚拟户为空 或者 真实户不为空 则说明客户端token缓存有问题 需要重新登录
                if (userQueryOutput == null || userQueryOutput.getUserBaseDemo() == null) {
                    //需要自动注册
                    UserRegisterInput userRegisterInput = new UserRegisterInput();
                    userRegisterInput.setUserRegisterTypeEnum(UserRegisterTypeEnum.IMEI);
                    userRegisterInput.setEmail("");
                    userRegisterInput.setOpenId("");
                    userRegisterInput.setDeviceId(requestBasicInfo.getDeviceId());
                    userRegisterInput.setPassword("");
                    userRegisterInput.setAppCode(requestBasicInfo.getAppCode());
                    userRegisterInput.setPackageName(requestBasicInfo.getPackageName());
                    userRegisterInput.setClientId(requestBasicInfo.getClientId());
                    userRegisterInput.setPlatform(requestBasicInfo.getPlatform());
                    userRegisterInput.setVersion(requestBasicInfo.getAppVersion());
                    userRegisterInput.setVersionCode(requestBasicInfo.getVersionCode());
                    userRegisterInput.setChannel(requestBasicInfo.getChannel());
                    userRegisterInput.setIp(requestBasicInfo.getIp());
                    userRegisterService.deviceRegister(userRegisterInput);
                    userQueryOutput = userQueryService.getUserInfoByDeviceId(requestBasicInfo.getAppCode(), requestBasicInfo.getDeviceId());

                    CreateAccountVO createAccountVO = new CreateAccountVO();
                    createAccountVO.setUserCode(userQueryOutput.getUserBaseDemo().getUserCode());
                    createAccountVO.setAccountTypeEnum(AccountTypeEnum.DEMO);
                    createAccountService.createAccount(createAccountVO);
                }
                userAccessInfo.setLoginMode(0);
                userAccessInfo.setUserCode(userQueryOutput.getUserBaseDemo().getUserCode());
                userAccessInfo.setUserBaseReal(userQueryOutput.getUserBaseReal());
                userAccessInfo.setUserBaseDemo(userQueryOutput.getUserBaseDemo());
                if (TradeTypeEnum.REAL_TRADE.getCode().equals(tradeType)) {
                    LOGGER.info("登录失败3,realToken:{}", realToken);
                    throw new ApplicationException(RestCodeEnum.NEED_TOKEN_ERROR);
                }
            }
            RequestLocalInfo.putCurrentUser(userAccessInfo);
            return true;
        } catch (Exception e) {
            LOGGER.info("登录token校验失败,realToken:{}", realToken, e);
            return false;
        }
    }

    private boolean isWebResources(HttpServletRequest request) {
        String uri = request.getRequestURI();
        uri = uri.replace("//", "/");
        if (uri.startsWith("/web/") || uri.contains("swagger") || uri.contains("api-docs")) {
            return true;
        }
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o,
                           ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
                                Object handler, Exception e) throws Exception {
    }

    public AccessInterceptor() {
    }

    private String getRequestBody(HttpServletRequest request) {

        String businessParams = "";
        try {
            RequestWrapper requestWrapper = new RequestWrapper(request);
            BufferedReader bufferedReader = requestWrapper.getReader();
            String line;
            StringBuilder requestBody = new StringBuilder();
            while ((line = bufferedReader.readLine()) != null) {
                requestBody.append(line);
            }
            if (StringUtils.isNotEmpty(requestBody.toString())) {
                return new String(requestBody);
            }
//            InputStream inputStream = request.getInputStream();
//            businessParams = IOUtils.toString(inputStream, Charset.defaultCharset());
//            if (StringUtils.isNotEmpty(businessParams)) {
//                return businessParams;
//            }
        } catch (Exception e) {
            throw new ApplicationException(RestCodeEnum.PARAM_RESOLVE_ERROR);
        }
        return businessParams;
    }

    private Integer getTradeType(HttpServletRequest request) {
        Integer tradeType = null;
        String businessJson = getRequestBody(request);
        try {
            tradeType = JSONUtils.json2map(businessJson).get("tradeType") == null ? null : Integer.valueOf(JSONUtils.json2map(businessJson).get("tradeType").toString());
        } catch (Exception e) {
            LOGGER.error("========= Parameter parsing exception e:{}========" + e);
        }
        return tradeType;
    }
}
